﻿using System;
using System.Collections.Generic;
using System.Text;
using ShootSearch.Core;
using ShootSearch.Common;
using System.Text.RegularExpressions;
using ShootSearch.Util;
using System.Diagnostics;
using ShootSearch.FileSystem;
using System.ComponentModel;
using log4net;

namespace ShootSearch.Parsers
{
    public class RegexParser : IParser
    {
        public const string SOURCE = "Source";

        protected RegexParserConfig config = new RegexParserConfig();

        /// <summary>
        /// 
        /// </summary>
        public RegexParser()
        {
            
            ContentType = new List<string>();
            ContentType.Add("text/html");
            ContentType.Add("text/xhtml");
            ContentType.Add("text/plain");
        }

        

        #region IConfigable Members

        public SpiderManConfig Config
        {
            get { return config; }
            set
            {
                if (value is RegexParserConfig)
                    config = value as RegexParserConfig;
            }
        }

        public ILog Logger { get; set; }

        #endregion

        #region IFilter Members

        public List<string> ContentType { get; protected set; }

        public bool Parse(IFileObject input, out IFileObject output)
        {
            output = null;
            if (config == null) return false;
            string pageSource = input.Encoding.GetString(input.Content);

            FileObjectText item = new FileObjectText(input.Url, input.DateTime);


            for (int i = 0; i < config.Rules.Count; i++)
            {
                PageRule rule = config.Rules[i];
                bool matchUrl = rule.Rule.IsMatch(input.Url);

                bool matchPage = false;
                //Only url matched
                if ((rule.Type == PageRuleType.UrlOnly ||
                    rule.Type == PageRuleType.Either) && matchUrl)
                {
                    MatchRule(rule.Rule, ref pageSource, ref item);
                    if (!rule.CanContinue) break;
                }

                matchPage = rule.Rule.IsMatch(pageSource);

                //Only url matched
                if (rule.Type == PageRuleType.PageSourceOnly ||
                        rule.Type == PageRuleType.Either && matchPage)
                {
                    MatchRule(rule.Rule, ref pageSource, ref item);
                    if (!rule.CanContinue) break;

                }

                //Both url && pagesource
                if (rule.Type == PageRuleType.Both && matchUrl && matchPage)
                {
                    MatchRule(rule.Rule, ref pageSource, ref item);
                    if (!rule.CanContinue) break;
                }


            }
            if (item.Count > 0)
            {
                if(config.IncludeSource)
                    item.Add(SOURCE, input.Encoding.GetString(input.Content));
                output = item;

                return true;
            }
            else return false;


        }

        #endregion

        #region MatchRule
        protected void MatchRule(TreeRule rule, ref string pageSource, ref FileObjectText item)
        {
            List<TreeRule> rules = rule.Children;
            for (int i = 0; i < rules.Count; i++)
            {
                Match match = rules[i].Match(pageSource);
                if (!string.IsNullOrEmpty(rules[i].GroupName)
                    && match.Groups[rules[i].GroupName].Success)
                {
                    string value = match.Groups[rules[i].GroupName].Value;
                    //Filters
                    for (int j = 0; j < rule.Filters.Count; j++)
                    {
                        value = rule.Filters[j].Replace(value, rule.Filters[j].Replacement);
                    }

                    //Convert html to text
                    if (!rules[i].OutputHtml)
                    {
                        value = WebPage.Html2Text(value);
                    }
                    //Format to the output format
                    if (!string.IsNullOrEmpty(rules[i].OutputFormat))
                    {
                        try
                        {
                            value = string.Format(rules[i].OutputFormat, value);
                        }
                        catch (Exception ex)
                        {
                            Trace.WriteLine(ex);
                        }
                    }

                    item[rules[i].GroupName] = value;


                }

                //Continue to match the sub rules
                if (match.Success && rules[i].Children.Count > 0)
                {
                    string subSource = match.Value;
                    MatchRule(rules[i], ref subSource, ref item);
                }
            }
        }
        #endregion

    }


    #region RegexFilterConfig
    [Serializable]
    [TypeConverter(typeof(ExpandableConverter<RegexParserConfig>))]
    public class RegexParserConfig : SpiderManConfig
    {
        public bool IncludeSource { get; set; }
        public List<PageRule> Rules { get; set; }

        public RegexParserConfig()
        {
            Rules = new List<PageRule>();
        }
    }
    
    #endregion

}
